<!DOCTYPE html><html class="appearance-light" lang="zh-CN"><head><meta charset="UTF-8"><title>Go tour Go 语言之旅 练习题</title><meta name="description" content="如你所见, 希望做个能言善道的人"><meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, initial-scale=1"><!-- Google Analytics --><!-- End Google Analytics -->
<!-- Baidu Analytics --><!-- End Baidu Analytics --><link rel="icon" href="/images/touxiang.jpg"><link rel="stylesheet" href="/style/common/bulma.css"><link rel="stylesheet" href="/style/base.css"><link rel="stylesheet" href="/style/icon.css"><link rel="stylesheet" href="/style/common/helper.css"><script src="/js/common.js"></script><link rel="stylesheet" href="/style/post.css"><link rel="stylesheet" href="/style/themes/highlight-theme-light.css"><script src="/js/highlight.pack.js"></script><meta name="description" content="记: 被 Go 的协程和 channel 弄晕的一天  
接下来用 Go 做个简单的 RPC 框架 (大坑


练习：循环与函数func Sqrt(x float64) float64 &amp;#123;
    // init
    z := x / 2
    pre := x
    
    // newton
    for math.Abs(pre - z) &amp;gt; 1e-13 &amp;#123;
        pre = z
        z -= (z * z - x) / (2 * z)
        fmt.Println(z, pre)
    &amp;#125;
    
    return z
&amp;#125;

练习：切片func Pic(dx, dy int) [][]uint8 &amp;#1.."><meta name="generator" content="Hexo 5.4.0"></head><body class="is-flex is-flex-direction-column"><header class="header-widget is-flex-shrink-0 is-hidden-mobile"><div class="container is-fullhd is-flex is-justify-content-space-between is-align-items-center is-full-height"><section class="is-hidden-mobile is-flex-shrink-0"><h2><a href="/">insight's blog</a></h2></section><h3 class="is-hidden-mobile is-family-serif is-full-height is-flex is-align-items-center is-flex-shrink-0"><div class="is-full-height" id="postTopic"><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">Go tour Go 语言之旅 练习题</p><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">点击返回顶部</p></div></h3><aside class="is-flex-shrink-0"><h3 class="is-inline-block"><a href="/">首页</a></h3><h3 class="is-inline-block"><a href="/about">关于</a></h3><h3 class="is-inline-block"><a href="/archives">归档</a></h3></aside></div></header><header class="is-flex header-widget is-flex-shrink-0 is-align-items-center is-justify-content-center is-hidden-tablet"><h3 class="is-inline-block"><a href="/">首页</a></h3><h3 class="is-inline-block"><a href="/about">关于</a></h3><h3 class="is-inline-block"><a href="/archives">归档</a></h3></header><main><main class="container is-max-widescreen content section post-page pt-4 px-4"><div class="columns is-flex-desktop is-justify-content-center is-flex-direction-row-reverse"><div class="column is-3 is-hidden-mobile"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9A%E5%BE%AA%E7%8E%AF%E4%B8%8E%E5%87%BD%E6%95%B0"><span class="toc-text">练习：循环与函数</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9A%E5%88%87%E7%89%87"><span class="toc-text">练习：切片</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9A%E6%98%A0%E5%B0%84"><span class="toc-text">练习：映射</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9A%E6%96%90%E6%B3%A2%E7%BA%B3%E5%A5%91%E9%97%AD%E5%8C%85"><span class="toc-text">练习：斐波纳契闭包</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9AStringer"><span class="toc-text">练习：Stringer</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9AError"><span class="toc-text">练习：Error</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9AReader"><span class="toc-text">练习：Reader</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9Arot13Reader"><span class="toc-text">练习：rot13Reader</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9AImage"><span class="toc-text">练习：Image</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9A%E7%AD%89%E4%BB%B7%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91"><span class="toc-text">练习：等价二叉查找树</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%83%E4%B9%A0%EF%BC%9AWeb-%E7%88%AC%E8%99%AB"><span class="toc-text">练习：Web 爬虫</span></a></li></ol></div><div class="column is-9"><header class="my-4"></header><h1 class="mt-0 mb-1 is-family-serif" id="postTitle">Go tour Go 语言之旅 练习题</h1><time class="has-text-grey" datetime="2021-09-05T15:06:22.000Z">2021-09-05</time><article class="mt-2 post-content"><p>记: 被 Go 的协程和 channel 弄晕的一天  </p>
<p>接下来用 Go 做个简单的 RPC 框架 (大坑</p>
<span id="more"></span>

<h2 id="练习：循环与函数"><a href="#练习：循环与函数" class="headerlink" title="练习：循环与函数"></a>练习：循环与函数</h2><pre><code class="go">func Sqrt(x float64) float64 &#123;
    // init
    z := x / 2
    pre := x
    
    // newton
    for math.Abs(pre - z) &gt; 1e-13 &#123;
        pre = z
        z -= (z * z - x) / (2 * z)
        fmt.Println(z, pre)
    &#125;
    
    return z
&#125;
</code></pre>
<h2 id="练习：切片"><a href="#练习：切片" class="headerlink" title="练习：切片"></a>练习：切片</h2><pre><code class="go">func Pic(dx, dy int) [][]uint8 &#123;
    ret := make([][]uint8, dy)
    
    for i := 0; i &lt; dy; i++ &#123;
        t := make([]uint8, dx)
        for j := 0; j &lt; dx; j++ &#123;
            // awesome i + j i * j i &amp; j i &lt;&lt; j
            t[j] = uint8(i ^ j)
        &#125;
        ret[i] = t
    &#125;
    
    return ret
&#125;
</code></pre>
<h2 id="练习：映射"><a href="#练习：映射" class="headerlink" title="练习：映射"></a>练习：映射</h2><pre><code class="go">func WordCount(s string) map[string]int &#123;
    ret := make(map[string]int)
    
    cs := strings.Fields(s)
    for _, v := range cs &#123;
        ret[v]++
    &#125;
    
    return ret
&#125;
</code></pre>
<h2 id="练习：斐波纳契闭包"><a href="#练习：斐波纳契闭包" class="headerlink" title="练习：斐波纳契闭包"></a>练习：斐波纳契闭包</h2><pre><code class="go">// 返回一个“返回int的函数”
func fibonacci() func() int &#123;
    f1, f2 := 0, 1
    return func() int &#123;
        ret := f1
        f1, f2 = f2, f2 + ret
        return ret
    &#125;
&#125;
</code></pre>
<h2 id="练习：Stringer"><a href="#练习：Stringer" class="headerlink" title="练习：Stringer"></a>练习：Stringer</h2><pre><code class="go">// TODO: 给 IPAddr 添加一个 &quot;String() string&quot; 方法
func (ip IPAddr) String() string &#123;
    return fmt.Sprintf(&quot;%v.%v.%v.%v&quot;, ip[0], ip[1], ip[2], ip[3])
&#125;
</code></pre>
<h2 id="练习：Error"><a href="#练习：Error" class="headerlink" title="练习：Error"></a>练习：Error</h2><pre><code class="go">type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string &#123;
    // 为了打印 e 本身, 会调用 Error()
    // Error() 中又再次打印 e, 所以死循环了
    // fmt.Sprintf(&quot;cannot Sqrt negative number: %v&quot;, e)
    return fmt.Sprintf(&quot;cannot Sqrt negative number: %v&quot;, float64(e))
&#125;

func Sqrt(x float64) (float64, error) &#123;
    if x &lt; 0 &#123;
        return x, ErrNegativeSqrt(x)
    &#125;
    
    // init
    z := x / 2
    pre := x
    
    // newton
    for math.Abs(pre - z) &gt; 1e-13 &#123;
        pre = z
        z -= (z * z - x) / (2 * z)
    &#125;
    
    return z, nil
&#125;
</code></pre>
<h2 id="练习：Reader"><a href="#练习：Reader" class="headerlink" title="练习：Reader"></a>练习：Reader</h2><pre><code class="go">import &quot;golang.org/x/tour/reader&quot;

type MyReader struct&#123;&#125;

// TODO: 给 MyReader 添加一个 Read([]byte) (int, error) 方法
// 使用 &#39;A&#39; 填充 b 这个字节数组
func (r MyReader) Read(b []byte) (n int, err error) &#123;
    blen := len(b)
    
    for i := 0; i &lt; blen; i++ &#123;
        b[i] = &#39;A&#39;
    &#125;
    
    return blen, nil
&#125;
</code></pre>
<h2 id="练习：rot13Reader"><a href="#练习：rot13Reader" class="headerlink" title="练习：rot13Reader"></a>练习：rot13Reader</h2><pre><code class="go">type rot13Reader struct &#123;
    r io.Reader
&#125;

func (rot rot13Reader) Read(b []byte) (n int, err error) &#123;
    n, err = rot.r.Read(b)
    if err != nil &#123;
        return n, err
    &#125;
    
    // 修改 v 无效
    for i, v := range b &#123;
        switch &#123;
            case &#39;a&#39; &lt;= v &amp;&amp; v &lt;= &#39;m&#39; || &#39;A&#39; &lt;= v &amp;&amp; v &lt;= &#39;M&#39;:
                b[i] += 13;
            case &#39;n&#39; &lt;= v &amp;&amp; v &lt;= &#39;z&#39; || &#39;N&#39; &lt;= v &amp;&amp; v &lt;= &#39;Z&#39;:
                b[i] -= 13
        &#125;
    &#125;
    
    return n, err
&#125;

func main() &#123;
    s := strings.NewReader(&quot;Lbh penpxrq gur pbqr!&quot;)
    r := rot13Reader&#123;s&#125;
    // ret: You cracked the code!
    io.Copy(os.Stdout, &amp;r)
&#125;
</code></pre>
<h2 id="练习：Image"><a href="#练习：Image" class="headerlink" title="练习：Image"></a>练习：Image</h2><pre><code class="go">package main

import (
    &quot;golang.org/x/tour/pic&quot;
    &quot;image&quot;
    &quot;image/color&quot;
)

type Image struct&#123;
    w int // width
    h int // high
&#125;

// ColorModel() color.Model
// Bounds() Rectangle
// At(x, y int) color.Color
func (img Image) ColorModel() color.Model &#123;
    return color.RGBAModel
&#125;

func (img Image) Bounds() image.Rectangle &#123;
    return image.Rect(0, 0, img.w, img.h)
&#125;

func (img Image) At(x, y int) color.Color &#123;
    // 自定义要产生的颜色
    return color.RGBA&#123;uint8(x + y), uint8(x * y), uint8(x &amp; y), uint8(x ^ y)&#125;
&#125;

func main() &#123;
    // 画一个颜色为rgba(x + y, x - y, x * y, x &amp; y)的方块
    m := Image&#123;100, 100&#125;
    pic.ShowImage(m)
&#125;
</code></pre>
<h2 id="练习：等价二叉查找树"><a href="#练习：等价二叉查找树" class="headerlink" title="练习：等价二叉查找树"></a>练习：等价二叉查找树</h2><pre><code class="go">package main

import (
    &quot;fmt&quot;
    &quot;golang.org/x/tour/tree&quot;
)

// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。
func Walk(t *tree.Tree, ch chan int) &#123;
    if t == nil &#123;
        return
    &#125;
    Walk(t.Left, ch)
    ch &lt;- t.Value
    Walk(t.Right, ch)
&#125;

// Same 检测树 t1 和 t2 是否含有相同的值。
func Same(t1, t2 *tree.Tree) bool &#123;
    ch1 := make(chan int, 10)
    go Walk(t1, ch1)
    ch2 := make(chan int, 10)
    go Walk(t2, ch2)
    
    for i := 0; i &lt; 10; i++ &#123;
        v1, v2 := &lt;-ch1, &lt;-ch2
        if v1 != v2 &#123;
            return false
        &#125;
    &#125;
    
    return true
&#125;

func main() &#123;
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
&#125;
</code></pre>
<h2 id="练习：Web-爬虫"><a href="#练习：Web-爬虫" class="headerlink" title="练习：Web 爬虫"></a>练习：Web 爬虫</h2><pre><code class="go">package main

import (
    &quot;fmt&quot;
    &quot;sync&quot;
)

type Fetcher interface &#123;
    // Fetch 返回 URL 的 body 内容，并且将在这个页面上找到的 URL 放到一个 slice 中。
    Fetch(url string) (body string, urls []string, err error)
&#125;

type SafeUrlSet struct &#123;
    set    Set
    mux    sync.RWMutex
&#125;

type Set map[string]bool

var urlSet = SafeUrlSet&#123;make(Set), sync.RWMutex&#123;&#125;&#125;

// 访问结束标志
var end = make(chan bool)

// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面，直到达到最大深度。
func Crawl(url string, depth int, fetcher Fetcher) &#123;
    if depth &lt;= 0 &#123;
        return
    &#125;

    // 使用 set 实现不重复抓取
    // 由于使用了协程 存在并发问题
    // 需要使用锁来同步 set 的访问
    // 一开始使用 Mutex 独占锁
    // 后来优化成读写锁
    
    // 先读取值
    urlSet.mux.RLock()
    vis := urlSet.set[url]
    urlSet.mux.RUnlock()
    
    if vis &#123;
        return
    &#125;
    
    // 再修改值
    urlSet.mux.Lock()
    urlSet.set[url] = true
    urlSet.mux.Unlock()
    
    body, urls, err := fetcher.Fetch(url)
    if err != nil &#123;
        fmt.Println(err)
        return
    &#125;
    fmt.Printf(&quot;found: %s %q\n&quot;, url, body)
    for _, u := range urls &#123;
        // 使用 go 关键字 并行抓取
        go Crawl(u, depth-1, fetcher)
    &#125;

    end &lt;- true
&#125;

func Daemon() &#123;
    // 用于等待所有协程结束再结束 main 线程, 一共有五条 url
    availableUrl := 5
    for i := 1; i &lt; availableUrl; i++ &#123;
        &lt;- end
    &#125;
&#125;

func main() &#123;
    // 用 go 修饰 
    // 原因: 1. 并行抓取 2. 否则 channel 会阻塞
    go Crawl(&quot;https://golang.org/&quot;, 4, &amp;fetcher)
    Daemon()
&#125;

// fakeFetcher 是返回若干结果的 Fetcher。
type fakeFetcher map[string]*fakeResult

type fakeResult struct &#123;
    body string
    urls []string
&#125;

func (f *fakeFetcher) Fetch(url string) (string, []string, error) &#123;
    if res, ok := (*f)[url]; ok &#123;
        return res.body, res.urls, nil
    &#125;
    return &quot;&quot;, nil, fmt.Errorf(&quot;not found: %s&quot;, url)
&#125;

// fetcher 是填充后的 fakeFetcher。
var fetcher = fakeFetcher&#123;
    &quot;https://golang.org/&quot;: &amp;fakeResult&#123;
        &quot;The Go Programming Language&quot;,
        []string&#123;
            &quot;https://golang.org/pkg/&quot;,
            &quot;https://golang.org/cmd/&quot;,
        &#125;,
    &#125;,
    &quot;https://golang.org/pkg/&quot;: &amp;fakeResult&#123;
        &quot;Packages&quot;,
        []string&#123;
            &quot;https://golang.org/&quot;,
            &quot;https://golang.org/cmd/&quot;,
            &quot;https://golang.org/pkg/fmt/&quot;,
            &quot;https://golang.org/pkg/os/&quot;,
        &#125;,
    &#125;,
    &quot;https://golang.org/pkg/fmt/&quot;: &amp;fakeResult&#123;
        &quot;Package fmt&quot;,
        []string&#123;
            &quot;https://golang.org/&quot;,
            &quot;https://golang.org/pkg/&quot;,
        &#125;,
    &#125;,
    &quot;https://golang.org/pkg/os/&quot;: &amp;fakeResult&#123;
        &quot;Package os&quot;,
        []string&#123;
            &quot;https://golang.org/&quot;,
            &quot;https://golang.org/pkg/&quot;,
        &#125;,
    &#125;,
&#125;
</code></pre>
</article><section class="jump-container is-flex is-justify-content-space-between my-6"><!-- em is empty placeholder--><em></em><a class="button is-default" href="/archives/3753999883/" title="Hexo next主题 更新社交链接图标 引入iconfont阿里巴巴矢量图标库"><span class="has-text-weight-semibold">下一页: Hexo next主题 更新社交链接图标 引入iconfont阿里巴巴矢量图标库</span><i class="iconfont icon-next ml-2 has-text-grey"></i></a></section><article class="mt-6 comment-container"><script async src="https://utteranc.es/client.js" issue-term="pathname" theme="preferred-color-scheme"></script></article></div></div></main></main><footer class="is-flex is-flex-direction-column is-align-items-center is-flex-shrink-0 is-family-serif"><section class="sns-container"><!-- Github--><a title="github" target="_blank" rel="noopener nofollow" href="//github.com/insight-21"><i class="iconfont icon-github"></i></a><!-- Gitee--><a title="gitee" target="_blank" rel="noopener nofollow" href="//gitee.com/insight21"><i class="iconfont icon-gitee"></i></a><!-- Ins--><!-- RSS--><!-- 知乎--><!-- 领英--><!-- 脸书--></section><p><span>Copyright ©</span><span> insight 2021</span></p><div class="is-flex is-justify-content-center is-flex-wrap-wrap"><p>Powered by Hexo &verbar;&nbsp;</p><p class="is-flex is-justify-content-center"><a title="Hexo theme author" target="_blank" rel="noopener" href="//github.com/haojen">Theme by Haojen&nbsp;</a></p><div style="margin-top: 2px"><a class="github-button" title="github-button" target="_blank" rel="noopener" href="https://github.com/haojen/hexo-theme-Claudia" data-color-scheme="no-preference: light; light: light; dark: dark;" data-show-count="true"></a></div></div><div><span></span></div></footer><script async defer src="https://buttons.github.io/buttons.js"></script><script src="/js/post.js"></script></body></html>